package com.codingday.app;

import java.util.ArrayList;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;

import com.codingday.app.MyLocation.LocationResult;
import com.google.code.microlog4android.Level;
import com.google.code.microlog4android.Logger;
import com.google.code.microlog4android.LoggerFactory;
import com.google.code.microlog4android.appender.FileAppender;
import com.google.code.microlog4android.appender.LogCatAppender;
import com.google.code.microlog4android.format.PatternFormatter;


import android.app.AlarmManager;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.location.Location;
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.SystemClock;
import android.util.Log;
import android.widget.Toast;


// Run in Background
// get current location from gps data
// compare with todo's location
// 
public class GeoAlarmService extends Service{

		private int NOTIFICATION = R.string.local_service_started;
	 	private NotificationManager mNM;
	
	 	private static GeoAlarmService instance = null;
	    
	    //private static Timer monitoringTimer = null;
	    //private static ScheduledExecutorService scheduler=null;// = Executors.newScheduledThreadPool(1);

	    //private final static ScheduledExecutorService schedule = Executors.newSingleThreadScheduledExecutor();
		static AlarmManager am;// = (AlarmManager)getSystemService(ALARM_SERVICE);
	    static Intent serviceIntent;
	    static PendingIntent sender;
		
	    static Runnable checker;
		private static LocationResult locationResult = null;
		private static MyLocation myLocation = null;
		private Location lastKnownLocation = null;

	    private final IBinder mBinder = new GeoAlarmBinder();
    	static int boundCount = 0;
    	static int checkCount = 0;
    	
    	final static int gpsWaitingTime = 10000;
    	
    	static public Logger logger = null;//LoggerFactory.getLogger ();

    	
    	public static int getServiceBoundCount(){
    		return boundCount;
    	}
    	
	    public class GeoAlarmBinder extends Binder{

	    	GeoAlarmService getService(){
	    		return GeoAlarmService.this;
	    	}
	    }
	    
		@Override
		public IBinder onBind(Intent intent) {
			boundCount++;
			Log.i("GeoAlarmService","GPSLoggerService.onBind() - boundCount : "+boundCount);
			return mBinder;
		}
	
		
		@Override
		public boolean onUnbind(Intent intent) {
			boundCount--;
			Log.i("GeoAlarmService","GPSLoggerService.onUnbind() - boundCount : "+boundCount);
			return super.onUnbind(intent);
		}


		public GeoAlarmService() {
			//mBinder = new GeoAlarmBinder();
			Log.i("GeoAlarmService","GPSLoggerService.GPSLoggerService().");
		}



		public static boolean isInstanceCreated() { 
		      return instance != null; 
		}

		@Override
		public void onCreate() {
			Log.i("GeoAlarmService","GPSLoggerService.onCreate().");
			instance = this;
			mNM = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
			//myLocationManager = new MyLocationManager(this);
			myLocation = new MyLocation();
			checkCount = 0;
			locationResult = new LocationResult(){
			    @Override
			    public void gotLocation(final Location location){
			    		lastKnownLocation = location;
			    		Log.i("GeoAlarmService","gotLocation!!!");
			    		if(logger != null){
			    			logger.info("GeoAlarmService - getLocation");
			    		}
			    		checkCount++;
				    	float minDistance = CheckTODOs(location);
				    	//float minDistance = 100;
				    	if(logger != null){
				    		logger.info("GeoAlarmService ("+checkCount+") - minDistnace : "+minDistance);
				    	}
						energySaving(minDistance);
						if(minDistance < 0){
							if(logger != null){
								logger.info("GeoAlarmService - Service Stop");
							}
							Log.i("GeoAlarmService","Stop!!");
						}else{
							Log.i("GeoAlarmService","trigger next monitoring timer");
							if(logger != null){
								logger.info("GeoAlarmService - trigger next monitoring timer");
							}
							//startMonitoringTimer();
							startMonitoringAlarmManager();
						}
			        };
			};
			
			//scheduler = Executors.newScheduledThreadPool(1);
			checker = new Runnable(){
				public void run() {
					LocationRequester();
			    }
			};
			
			
			// init alarm manager
			am = (AlarmManager)getSystemService(ALARM_SERVICE);

			super.onCreate();		
			
			// logger setting
			if(logger != null){
				PatternFormatter formatter = new PatternFormatter ();
				formatter.setPattern ( "%d {ISO8601} [%P] %m %T");
				logger.setLevel(Level.INFO);
				
				LogCatAppender logCatAppender = new LogCatAppender();
				logCatAppender.setFormatter (formatter);
				logger.addAppender(logCatAppender);
				
				FileAppender fileAppender = new FileAppender();
			    fileAppender.setAppend (true);
			    fileAppender.setFileName ( "GeoAlarmService.log");
			    fileAppender.setFormatter (formatter);
			    
			    logger.addAppender (fileAppender);
			}
			
			
		}
		public int getCheckCount(){
			return checkCount;
		}

		@Override
		public void onDestroy() {
			super.onDestroy();
			instance = null;
			Toast.makeText(getApplicationContext(), "GeoAlarm Service Stop", 1).show();
			 mNM.cancel(NOTIFICATION);
			 stopLoggingService();
			 am.cancel(sender);
		}

		@Override
		public void onStart(Intent intent, int startId) {
			Log.i("GeoAlarmService","GPSLoggerService.onStart().");

			startLoggingService();
            //startMonitoringTimer();
            startMonitoringAlarmManager();
			super.onStart(intent, startId);
		}

		
		public int onStartCommand(Intent intent, int flags, int startId) {
			Log.i("GeoAlarmService","GPSLoggerService.onStartCommand().");

			startLoggingService();
            //startMonitoringTimer();
            startMonitoringAlarmManager();
			return Service.START_STICKY;
		}

		
		// Notify
		private void showNotification(ArrayList<Integer> todoIDArray){
			CharSequence text;
			if(todoIDArray.size()>1){
				text = "You have "+todoIDArray.size()+" quests";
			}else{
				text = "You have a quest";
			}
			// show notification now
			Notification notification = new Notification(R.drawable.ic_stat_noti, text, System.currentTimeMillis());
			Intent intent = new Intent(this,TODONowList.class);
			intent.putExtra("positions", todoIDArray);
			intent.putExtra("fromNotification", true);
			//intent.putExtra("latitude", value);
			//intent.putExtra("longitude", value);
			//Log.i("Show Notification", "position : "+positionArray);
			PendingIntent contentIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
			//contentIntent.
			notification.flags = Notification.FLAG_AUTO_CANCEL;
			notification.setLatestEventInfo(this, "Remind The Milk", text, contentIntent);	// title, content
			
			// add sound, vibration and light
			notification.defaults |= Notification.DEFAULT_SOUND;
			notification.defaults |= Notification.DEFAULT_VIBRATE;
			//notification.defaults |= Notification.DEFAULT_LIGHTS;
			
			// send notify
			mNM.notify(NOTIFICATION, notification);
		}
		
		private void startLoggingService(){
		}
		
		private void stopLoggingService(){
			Toast.makeText(this, "Stop Service!", 1).show();
            stopSelf();
		}
		
		private float CheckTODOs(Location location){

			float minDistance = -1.0f;
			ArrayList<TODO> todolist = TODOListManager.getTODOList();
			if(todolist == null){
				return -1.0f;
			}
			//Location location = this.myLocationManager.getMyLocation();
			Log.i("GeoAlarmService","CheckTODOs() -TimerTask Run, todolistSize : " + todolist.size());
			//Log.i("GeoAlarmService","CheckTODOs() - Current Location : "+location.getLatitude()+","+location.getLongitude());
			
			if(location.getLatitude() == 0.0 && location.getLongitude() == 0.0){
				Log.i("GeoAlarmService","Both Latitud and Longitude are 0");
				return -1.0f;
			}
			
			ArrayList<Integer> todoIDArray = new ArrayList<Integer>();
			for(int i = 0; i < todolist.size(); i++){
				
				TODO todo = todolist.get(i);
				if(todo.isEnabled() == false){
					continue;
				}
				if(todo.isCompleted() == true){
					continue;
				}
				
				
				double todoLatitude = todo.getLatitude();
				double todoLongitude = todo.getLongitude();
				int radius = todo.getRadius();
				float distance = getDistanceTo(location, todoLatitude, todoLongitude);
				
				Log.i("GeoAlarmService","distance : "+distance+"m");

				Log.i("GeoAlarmService","minDistance : "+minDistance);
				
				
				
				////////////// new version
				if(distance <= radius){
					if(todo.isPausedByInitial() == true){
						// set mindistance
						distance = 11000; // set distance here
						if(minDistance == -1.0f || minDistance > distance){
							minDistance = distance;
						}
					}else{
						// add to todoIDArray
						todoIDArray.add(new Integer((int) todo.getID()));
					}
				}else{
					if(todo.isPausedByInitial() == true){
						// resume todo
						todo.resumeFromInitialPause(true);
					}else{
						// set mindistance
						if(minDistance == -1.0f || minDistance > distance){
							minDistance = distance - radius;
						}
					}
				}

//				///////////// old version
//				if(distance <= radius){
//					// inside radius
//					if( todo.isPausedByInitial() == false){
//						// if it is not paused
//						Log.i("GeoAlarmService","You are now neer : "+todo.getTitle());
//						todoIDArray.add(new Integer((int) todo.getID()));
//					}
//				}else{
//					// out of radius
//					if(todo.isPausedByInitial() == true && (distance > radius)){
//						// if it is paused
//						todo.resumeFromInitialPause(true);
//					}
//				}
//
//				// out of range, working todos
//				if((todo.isPausedByInitial() == false) && (distance > radius)){
//					if(minDistance == -1.0f || minDistance > distance){
//						minDistance = distance - radius;
//					}
//				}else if((todo.isPausedByInitial() == true) && (distance <= radius)){
//					// assume this distance as 11Km
//					distance = 11000; // set distance here
//					if(minDistance == -1.0f || minDistance > distance){
//						minDistance = distance;
//					}
//				}
				
				
			} // end of for
			
			if(todoIDArray.size() > 0){
				showNotification(todoIDArray);
			}
			
			return minDistance;
		}
		
		private void energySaving(float minDistance){
			
			//int minDistanceInt = (int)minDistance;
			int newUpdateTimeSec = GeoAlarmSetting.getInstance(getApplicationContext()).getServiceUpdateTime();
			if(minDistance == -1.0f){
				newUpdateTimeSec = 60 * 60 * 24; // 1 Day (24 Hours)
			}else if(minDistance > 100000){
				// 100Km
				newUpdateTimeSec = 60 * 60;	// 1 Hour ( 60 Min)
			}else if(minDistance > 50000){
				// 50Km
				newUpdateTimeSec = 60*20;	// 20 Min
			}else if(minDistance > 10000){
				// 10Km
				newUpdateTimeSec = 60 * 10; // 10 Min
			}else if(minDistance > 5000){
				// 5Km
				newUpdateTimeSec = 60 * 5;  // 5 Min
			}else if(minDistance > 1000){
				// 1Km
				newUpdateTimeSec = 60 * 3;  // 2 Min
			}else if(minDistance > 500){
				// 500m
				newUpdateTimeSec = 60;		// 1 Min (60 Sec)
			}else if(minDistance > 100){
				// 100m
				newUpdateTimeSec = 20;		// 20 Sec
			}else{
				// close
				newUpdateTimeSec = 10;
			}
			Log.i("GeoAlarmService","energySaving : time : "+newUpdateTimeSec*1000+"secs");
			GeoAlarmSetting.getInstance(getApplicationContext()).setServiceUpdateTime(newUpdateTimeSec*1000);// - loggingInterval);
			//myLocationManager.Enable(newUpdateTimeSec*1000);
			
		}
		public static GeoAlarmService getInstance(){
			return instance;
		}

		
//		private static synchronized void startMonitoringTimer(){
//			Log.i("GeoAlarmService","startMonitoringTimer");
//			/*
//			if(monitoringTimer == null){
//				monitoringTimer = new Timer();
//			}
//			*/
//			
//			if(scheduler == null || scheduler.isShutdown()){
//				scheduler = Executors.newScheduledThreadPool(1);
//			}
//			Log.i("GeoAlarmService", "Start Geo Alarm Service - Monitoring Timer");
//			logger.info("");
//			logger.info("GeoAlarmService - Start Monitoring TImer : interval : "+GeoAlarmSetting.getInstance(GeoAlarmService.getInstance()).getServiceUpdateTime() );
//			
//			
//			//monitoringTimer.schedule((TimerTask) checker,  GeoAlarmSetting.getInstance(GeoAlarmService.getInstance()).getServiceUpdateTime());
//			//final ScheduledFuture chckerHandle = scheduler.scheduleAtFixedRate(checker, 10, 10, java.util.concurrent.TimeUnit.SECONDS);
//			//scheduler.
//			scheduler.schedule(
//					checker
//					, GeoAlarmSetting.getInstance(GeoAlarmService.getInstance()).getServiceUpdateTime()/1000
//					, java.util.concurrent.TimeUnit.SECONDS);
//
//		}
		
		private static synchronized void startMonitoringAlarmManager(){
			Log.i("GeoAlarmService", "Start Geo Alarm Service - AlarmManager");
			if(logger != null){
				logger.info("");
				logger.info("GeoAlarmService - Start Monitoring TImer : interval : "+GeoAlarmSetting.getInstance(GeoAlarmService.getInstance()).getServiceUpdateTime() );
			}
			
			serviceIntent = new Intent(GeoAlarmService.getInstance(), GeoAlarmReciever.class);
			sender = PendingIntent.getBroadcast(GeoAlarmService.getInstance(), 0, serviceIntent, 0);
			
			
			long firstTime = SystemClock.elapsedRealtime();
			//SystemClock.
			//long firstTime = SystemClock.currentThreadTimeMillis();
			am.cancel(sender);
			am.set(AlarmManager.ELAPSED_REALTIME_WAKEUP , firstTime + GeoAlarmSetting.getInstance(GeoAlarmService.getInstance()).getServiceUpdateTime(), sender);
			Log.i("GeoAlarmService","Alarm Setted : "+GeoAlarmSetting.getInstance(GeoAlarmService.getInstance()).getServiceUpdateTime());
			//am.
		}
		
		public static void LocationRequester(){
			Log.i("GeoAlarmService","Scheduled Timer Task RUN!!");
			if(logger != null){
				logger.info("GeoAlarmService - Scheduled Timer Task RUN!!");
			}
			
			/*
			try {
				Looper.prepare();
			} catch (Exception e) {
				e.printStackTrace();
			}
			*/
			
			boolean requestSuccess = myLocation.getLocation(GeoAlarmService.getInstance(), locationResult, gpsWaitingTime);
			Log.i("GeoAlarmService", "myLocation getLocation Request : " + requestSuccess);
			if(logger != null){
				logger.info("GeoAlarmService - " + "myLocation getLocation Request : " + requestSuccess);
			}
			//Looper.loop();
			//Looper.
			return;
		}

		
		public Location getLastKnownLocation(){
			return lastKnownLocation;
		}
		
		public static synchronized void resetService(){
			/*
			monitoringTimer.cancel();
			monitoringTimer.purge();
			monitoringTimer = null;
			*/
			
			//scheduler.shutdownNow();
			//scheduler.shutdown();
			am.cancel(sender);
			//am.
			//am.
			GeoAlarmSetting.getInstance(GeoAlarmService.getInstance()).setServiceUpdateTime(5*1000);
			//startMonitoringTimer();
			startMonitoringAlarmManager();
		}

		
		public float getDistanceTo(Location location, double todoLatitude, double todoLongitude){
			float[] results = new float[10];
			//Location location = this.myLocationManager.getMyLocation();
			double latitude = location.getLatitude();
			double longitude = location.getLongitude();
			Location.distanceBetween(todoLatitude, todoLongitude, latitude, longitude, results);
			return results[0];
		}
}
